/*!
 * \file instruction.c
 * \brief Fichier des instructions.
 * \author PETIT Calypso
 * \version 1.0
 * \date 08 juin 2011
 */
#include <stdio.h>
#include "instruction.h"
#include "error.h"
#include "machine.h"

//! Forme imprimable des codes opérations
const char *cop_names[] = {"ILLOP", "NOP", "LOAD", "STORE", "ADD", "SUB", "BRANCH", "CALL", "RET", "PUSH", "POP", "HALT"};

//! Forme imprimable des conditions
const char *condition_names[] = {"NC", "EQ", "NE", "GT", "GE", "LT", "LE"};

//! Affichage de l'adressage d'une instruction (absolu ou indexé)
/*!
 * \param instr l'instruction dont on veut afficher l'adressage
 */
void print_adressage(Instruction instr){

	// Traiter le cas d'un adressage absolu
	if(!instr.instr_generic._indexed)

		// Afficher l'adresse absolue quand elle est différente de 0
		//if(instr.instr_absolute._address != 0)
			printf("@0x%04x", instr.instr_absolute._address);

		// Quand elle est égale à 0
		//else
		//	printf("@0x%#04x", instr.instr_absolute._address);

	// Traiter le cas d'un adressage indexé
	else

		// Afficher le déplacement et le regitre d'index
		printf("%d[R%02d]", instr.instr_indexed._offset, instr.instr_indexed._rindex);
}

//! Affichage de la partie variable de l'instruction : la valeur ou l'adressage
/*!
 * \param instr l'instruction dont on veut afficher la partie variable
 */
void print_partie_variable(Instruction instr){

	// Traiter le cas d'une valeur immédiate
	if(instr.instr_generic._immediate)

		// Afficher la valeur immédiate
		printf("#%d", instr.instr_immediate._value);

	// Traiter le cas d'un adressage
	else
		print_adressage(instr);
}

//! Vérifie que le code condition est correct
/*!
 * \param cond le code condition à tester
 * \return vrai si le code condition est correct false sinon
 */
bool verif_cond(unsigned cond){
	if(cond != NC && cond != EQ && cond != NE && cond != GT && cond != GE && cond != LT && cond != LE)
		return false;
	return true;
}

//! Impression d'une instruction sous forme lisible (désassemblage)
/*!
 * \param instr l'instruction à imprimer
 * \param addr son adresse
 */
void print_instruction(Instruction instr, unsigned addr){

	switch(instr.instr_generic._cop){

	// Traiter le cas des ILLOP, NOP, RET et HALT
	case ILLOP:
	case NOP:
	case RET:
	case HALT:
		// Afficher le code opération
		printf("%s ", cop_names[instr.instr_generic._cop]);

		break;
	
	// Traiter le cas des LOAD, ADD et SUB
	case LOAD:
	case ADD:
	case SUB:
		// Afficher le code opération
		printf("%s ", cop_names[instr.instr_generic._cop]);
		
		// Afficher le registre concerné
		printf("R%02d, ", instr.instr_generic._regcond);

		// Affichaer la partie variable
		print_partie_variable(instr);

		break;

	// Traiter le cas d'un STORE
	case STORE:
		// Afficher le code opération
		printf("%s ", cop_names[instr.instr_generic._cop]);

		// Afficher le registre concerné
		printf("R%02d, ", instr.instr_generic._regcond);
		
		// Afficher l'adressage
		print_adressage(instr);

		break;

	// Traiter le cas des BRANCH et CALL
	case BRANCH:
	case CALL:
		// Vérifier que la condition est correcte
		if(!verif_cond(instr.instr_generic._regcond))
			error(ERR_CONDITION, addr);

		// Afficher le code opération
		printf("%s ", cop_names[instr.instr_generic._cop]);

		// Afficher la condition
		printf("%s, ", condition_names[instr.instr_generic._regcond]);

		// Afficher l'adressage
		print_adressage(instr);

		break;
	
	// Traiter le cas d'un PUSH
	case PUSH:
		// Afficher le code opération
		printf("%s ", cop_names[instr.instr_generic._cop]);
	
		// Afficher la partie variable
		print_partie_variable(instr);

		break;

	// Traiter le cas d'un POP
	case POP:
		// Afficher le code opération
		printf("%s ", cop_names[instr.instr_generic._cop]);

		// Afficher l'adressage
		print_adressage(instr);

		break;
	
	// Traiter le cas d'une instruction inconnue
	default:
		error(ERR_UNKNOWN, addr);
	}
}
